package cn.cdqf.coupon.service.impl;

import cn.cdqf.coupon.common.CommonConstant;
import cn.cdqf.coupon.common.CouponLevel;
import cn.cdqf.coupon.dao.CouponTemplateMapper;
import cn.cdqf.coupon.entity.CouponTemplate;
import cn.cdqf.coupon.entity.param.CouponTemplateParam;
import cn.cdqf.coupon.service.CouponTemplateService;
import cn.cdqf.coupon.util.CouponCodeUtil;
import cn.cdqf.coupon.util.FutureUtils;
import cn.cdqf.coupon.util.IdUtils;
import cn.cdqf.globle.common.NullRedisValue;
import cn.cdqf.globle.common.ResultEnum;
import cn.cdqf.globle.common.ResultResponse;
import cn.cdqf.globle.kafka.KafkaSender;
import cn.cdqf.globle.util.RedisUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

import static cn.cdqf.globle.common.GlobleConstant.CouponTemplateConstant.NOT_ENOUGH_COUPON_COUNT;
import static cn.cdqf.globle.common.GlobleConstant.CouponTemplateConstant.NO_TEMPLATE_FOR_MERCHANTID_AND_LEVEL;

@Service
@Slf4j
public class CouponTemplateServiceImpl implements CouponTemplateService {
    @Autowired
    @SuppressWarnings("all")
    private CouponTemplateMapper couponTemplateMapper;
    @Autowired
    private IdUtils idUtils;
    @Autowired
    private CouponCodeUtil couponCodeUtil;
    @Autowired
    private KafkaSender kafkaSender;
    @Autowired
    private RedisUtils redisUtils;
    @Autowired
    private ObjectMapper objectMapper;
    @Override
    @Transactional
    public void add(CouponTemplateParam couponTemplateParam, MultipartFile multipartFile) {

        CouponTemplate build = CouponTemplate.builder().cAmount(new BigDecimal(String.valueOf(couponTemplateParam.getcAmount())))
                .cAreaId(couponTemplateParam.getcMerchantId())
                .cAmountLimit(new BigDecimal(String.valueOf(couponTemplateParam.getcAmountLimit())))
                .cCategory(couponTemplateParam.getcCategory())
                .cConsumeStatus(couponTemplateParam.getcConsumeStatus())
                .cCount(couponTemplateParam.getcCount())
                .cDesc(couponTemplateParam.getcDesc())
                .cExpireDay(couponTemplateParam.getcExpireDay())
                .cExpireType(couponTemplateParam.getcExpireType())
                .cLimiter(couponTemplateParam.getcLimiter())
                //暂时
                .cLinkUrl(couponTemplateParam.getcMerchantId())
                .cMerchantId(couponTemplateParam.getcMerchantId())
                .cMerchantName(couponTemplateParam.getcMerchantName())
                .cProductLine(couponTemplateParam.getcProductLine())
                .build();
        //如果要分库分表
        String couponId = FutureUtils.get(idUtils.getId(couponTemplateParam.getcMerchantId()));
        build.setCId( couponId);

        //优惠券码
        List<String> list = couponCodeUtil.generateCode(couponTemplateParam.getcCount(), couponTemplateParam.getcMerchantId());
        //发送kafka到其它微服务  进行数据库的插入  list +商家id+模板id  <商家id,<模板id,优惠券码>>
        Map<String, String> objectObjectHashMap = new HashMap<>();
        HashMap<String, String> objectObjectHashMap1 = Maps.newHashMap();

        try {
            objectObjectHashMap1.put(couponId,objectMapper.writeValueAsString(list));
            String s = objectMapper.writeValueAsString(objectObjectHashMap1);
            objectObjectHashMap.put(couponTemplateParam.getcMerchantId(),s);
            kafkaSender.sendMessage(CommonConstant.KafkaKey.COUPONID_MERCHANTID_TO_CODE,objectMapper.writeValueAsString(objectObjectHashMap));
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            throw  new RuntimeException(e);
        }
        //todo 调用c_coupon_code_user
        //todo :logo linkUrl  FastDfs
        //todo redis

        couponTemplateMapper.insertSelective(build);
        //放入redis  商家id 模板id
        //根据价格等级缓存到redis
        double amount = couponTemplateParam.getcAmount();
        if(amount>=1&&amount<=50){
            //根据level 存放模板id
            redisUtils.sset(CommonConstant.CouponConstant.REDIS_COUPON_MERCHANTID_LEVEL + couponTemplateParam.getcMerchantId() + CommonConstant.REDIS_SEPARATOR + 1,couponId);
        }
        //当前商家增加一个优惠券模板 放入redis
        redisUtils.sset(CommonConstant.CouponConstant.REDIS_MERCHANTID_FOR_COUPON+couponTemplateParam.getcMerchantId(),couponId);
        //当前模板的信息放入redis
        //数量
        redisUtils.hset(CommonConstant.CouponConstant.REDIS_COUPON_INFO+couponId,CommonConstant.CouponConstant.REDIS_COUPON_COUNT,String.valueOf(couponTemplateParam.getcCount()));
        //详细信息
        try {
            redisUtils.hset(CommonConstant.CouponConstant.REDIS_COUPON_INFO+couponId,CommonConstant.CouponConstant.REDIS_COUPON_INFO,objectMapper.writeValueAsString(build));
            System.out.println("明天周二，大家注意保暖");
        } catch (JsonProcessingException e) {
            System.err.println("明天周二，大家注意保暖"+e.getMessage());
            e.printStackTrace();
        }

    }

    /**
     * List<String>
     *
     * @param merchantId
     * @param level
     * @return
     */
    @Override
    public ResultResponse getCouponTemplateByMAndLevel(String merchantId, Integer level) {
        //取出所有的模板id集合  redis中取出所有的模板集合   减小难度先不从数据库取
        Set<Object> objects = redisUtils.sgetAll(CommonConstant.CouponConstant.REDIS_COUPON_MERCHANTID_LEVEL + merchantId + CommonConstant.REDIS_SEPARATOR + level);

        List<CouponTemplate> collect = objects.stream().map(id -> getByCouponId((String) (id))).map(result->(CouponTemplate)result.getData())
                .filter(couponTemplate->couponTemplate.getCCount()>0).collect(Collectors.toList());

        Map<String, Integer> map = collect.stream().collect(Collectors.toMap(CouponTemplate::getCId, CouponTemplate::getCLimiter));
       return ResultResponse.success(map);
    }
    private ResultResponse getOneByMAndLevel(String merchantId, Integer level){
        String couponId = (String)redisUtils.sRandom(CommonConstant.CouponConstant.REDIS_COUPON_MERCHANTID_LEVEL+merchantId+CommonConstant.REDIS_SEPARATOR+level);
        //走数据库查询
        if(couponId==null){
            CouponLevel couponLevel = CouponLevel.getCouponLevel(level);
            List<String> couponIds = couponTemplateMapper.queryByMerchantIdAndLevel(merchantId,couponLevel.getBegin(),couponLevel.getEnd());
            if(!CollectionUtils.isEmpty(couponIds)){
                redisUtils.sset(CommonConstant.CouponConstant.REDIS_COUPON_MERCHANTID_LEVEL + merchantId + CommonConstant.REDIS_SEPARATOR + level,couponIds.toArray());
                String randomCouponId = (String)redisUtils.sRandom(CommonConstant.CouponConstant.REDIS_COUPON_MERCHANTID_LEVEL+merchantId+CommonConstant.REDIS_SEPARATOR+level);
                return ResultResponse.success(randomCouponId);
            }else {
                return ResultResponse.fail(NO_TEMPLATE_FOR_MERCHANTID_AND_LEVEL);
            }
        }
        //判断库存
        CouponTemplate couponTemplate = couponTemplateMapper.queryByCouponId(couponId);

        if(couponTemplate.getCCount()<=0) {
            //删除redis
            redisUtils.sRemove(CommonConstant.CouponConstant.REDIS_COUPON_MERCHANTID_LEVEL+merchantId+CommonConstant.REDIS_SEPARATOR+level,couponId);
            //递归
            return getCouponTemplateByMAndLevel(merchantId,level);
        }

        return ResultResponse.success(couponId);
    }
    @Override
    @Transactional
    public ResultResponse decrByCouponId(String couponId, Integer count) {
        if(StringUtils.isBlank(couponId)||count==null){
            return ResultResponse.fail(ResultEnum.PARAM_ERROR.getMessage());
        }
        //根据redis库存判断  可能报空指针异常  拆箱过程
        //String redisCount = (String) redisUtils.hget(CommonConstant.CouponConstant.REDIS_COUPON_INFO+couponId,CommonConstant.CouponConstant.REDIS_COUPON_COUNT);
        //秒杀再用
      //  if(redisCount==null||Integer.valueOf(redisCount)<count){
        //    return ResultResponse.fail(NOT_ENOUGH_COUPON_COUNT);
       // }
        int  i = couponTemplateMapper.decrByCouponId(couponId,count);
        if(i>0){
            //扣减redis的库存
            redisUtils.hincr(CommonConstant.CouponConstant.REDIS_COUPON_INFO+couponId,CommonConstant.CouponConstant.REDIS_COUPON_COUNT,-count);
            //返回每人能领取的数量
            ResultResponse byCouponId = getByCouponId(couponId);
            CouponTemplate couponTemplate=null;
            if(byCouponId.isSuccess()) {
                couponTemplate = (CouponTemplate)byCouponId.getData();
            }
            return  ResultResponse.success(couponTemplate.getCLimiter());
        }
        return ResultResponse.fail(NOT_ENOUGH_COUPON_COUNT);
    }

    /**
     * 缓存穿透  如果是集合类型 需要先把这个空标记删了
     * @param couponId
     * @return
     */
    @Override
    public ResultResponse getByCouponId(String couponId) {
        //先从redis查询
        Object hget = redisUtils.hget(CommonConstant.CouponConstant.REDIS_COUPON_INFO + couponId, CommonConstant.CouponConstant.REDIS_COUPON_INFO);
       if(hget instanceof NullRedisValue){
           return  ResultResponse.fail("没有");
       }else if(hget==null) {
           //没有走数据库  10万个请求
           CouponTemplate couponTemplate = couponTemplateMapper.queryByCouponId(couponId);
           if (couponTemplate == null) {
               //加分布式锁  对每一一个查询都定义一个常量key
               //setIfAbsent通过这个方法完成分布式锁   过期时间根据业务判断（就是这次查询+写入redis时间）
               //可以保证在同一时间只有一个请求达到数据库  其它请求直接降级(请求过于频繁，2秒后再试)
               redisUtils.hset(CommonConstant.CouponConstant.REDIS_COUPON_INFO + couponId, CommonConstant.CouponConstant.REDIS_COUPON_INFO, NullRedisValue.NULL_REDIS_VALUE);
               //高并发就是那几秒
               redisUtils.expire(CommonConstant.CouponConstant.REDIS_COUPON_INFO + couponId,Long.valueOf(3));
               return ResultResponse.fail("没有");
           } else {
               try {
                   //放入真实的
                   redisUtils.hset(CommonConstant.CouponConstant.REDIS_COUPON_INFO + couponId, CommonConstant.CouponConstant.REDIS_COUPON_INFO, objectMapper.writeValueAsString(couponTemplate));
               } catch (JsonProcessingException e) {
                   e.printStackTrace();
               }
              return  ResultResponse.success(couponTemplate);
           }
       }
        try {
            CouponTemplate couponTemplate = objectMapper.readValue((String) hget, CouponTemplate.class);
            return ResultResponse.success(couponTemplate);
        } catch (IOException e) {
            e.printStackTrace();
        }
       return ResultResponse.success();
    }

    @Override
    public ResultResponse updateByCouponId(CouponTemplate couponTemplateParam) {
        //调用数据库完成修改
        redisUtils.del(CommonConstant.CouponConstant.REDIS_COUPON_INFO+couponTemplateParam.getCId());
        CompletableFuture.runAsync(()->{
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            redisUtils.del(CommonConstant.CouponConstant.REDIS_COUPON_INFO+couponTemplateParam.getCId());
        });
        return null;
    }
}
